﻿using System;
using System.Collections.Generic;

namespace SimpleSpreadsheet.Core.Expressions
{
    /// <summary>
    /// Represents a cell block expression
    /// </summary>
    public class ReferenceRangeExpression : BaseExpression
    {
        public ReferenceRangeExpression(string name)
        {
            Name = name;
            References = EnumerateRange();
        }

        public string Name { get; set; }
        public List<ReferenceExpression> References { get; private set; }

        /// <summary>
        /// Converts a cell block into a list of cell references
        /// </summary>
        /// <returns></returns>
        private List<ReferenceExpression> EnumerateRange()
        {
            string[] bounds = Name.Split(new[] {':'});
            if (bounds.Length != 2)
                throw new ArgumentException("Wrong format of cell range");

            var cells = new List<ReferenceExpression>();

            string leftBound = bounds[0];
            string rightBound = bounds[1];

            int leftColumn = GetColumn(leftBound);
            int leftRow = GetRow(leftBound);
            int rightColumn = GetColumn(rightBound);
            int rightRow = GetRow(rightBound);

            for (int x = leftColumn; x <= rightColumn; x++)
                for (int y = leftRow; y <= rightRow; y++)
                {
                    string cellName = GetCellNameByIndexes(y, x);
                    cells.Add(new ReferenceExpression(cellName));
                }

            return cells;
        }

        public override void Accept(ExpressionVisitor visitor)
        {
            visitor.Visit(this);
        }

        #region Methods for working with cell addresses/columns/rows conversion

        /// <summary>
        /// Get column index by cell name
        /// </summary>
        /// <param name="cellName"></param>
        /// <returns></returns>
        public static int GetColumn(string cellName)
        {
            int i = 0;
            while (i < cellName.Length)
            {
                if (Char.IsDigit(cellName[i]))
                    break;
                i++;
            }

            return ColumnNameToIndex(cellName.Substring(0, i));
        }

        /// <summary>
        /// Get row index by cell name
        /// </summary>
        /// <param name="cellName"></param>
        /// <returns></returns>
        public static int GetRow(string cellName)
        {
            int i = 0;
            while (i < cellName.Length)
            {
                if (Char.IsDigit(cellName[i]))
                    break;
                i++;
            }

            return Int32.Parse(cellName.Substring(i)) - 1;
        }

        /// <summary>
        /// Get column index by column name
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int ColumnNameToIndex(string s)
        {
            int num = 0;
            for (int i = 0; i < s.Length; i++)
            {
                char c = s[i];
                if (!char.IsLetter(c))
                {
                    return num;
                }
                num *= 0x1a;
                num += c - 'A';
            }
            return num;
        }

        /// <summary>
        /// Get column name by column index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public static string ColumnIndexToName(int index)
        {
            if (index == 0)
            {
                return "A";
            }
            string str = "";
            for (int i = index; i >= 0; i = (i/0x1a) - 1)
            {
                int num2 = i%0x1a;
                var ch = (char) (0x41 + num2);
                str = ch + str;
            }
            return str;
        }

        /// <summary>
        /// Get cell name by row and column indexes
        /// </summary>
        /// <param name="row"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public static string GetCellNameByIndexes(int row, int column)
        {
            return String.Concat(ColumnIndexToName(column), row + 1);
        }

        #endregion
    }
}